home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / Boxes / Board.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-05  |  9.2 KB  |  459 lines  |  [TEXT/KAHL]

  1. /*
  2.  * Board.c - "Boxes" playing board manager
  3.  */
  4.  
  5. # include    "Boxes.h"
  6.  
  7.  
  8. # define    sideLen        30        /* should be even for best results */
  9. # define    dotSize        9        /* should be odd for best results */
  10. # define    linkWidth    3        /* should be odd for best results */
  11.  
  12.  
  13. /*
  14.  * Initial number of squares and dots horizontally/vertically.
  15.  */
  16.  
  17. short    hSquares = 5;
  18. short    vSquares = 5;
  19.  
  20. # define    hDots    (hSquares + 1)
  21. # define    vDots    (vSquares + 1)
  22.  
  23.  
  24. /*    square side bitmasks */
  25.  
  26. # define    leftMask        0x0001
  27. # define    topMask            0x0002
  28. # define    rightMask        0x0004
  29. # define    bottomMask        0x0008
  30. # define    allMask            0x000f        /* all sides */
  31.  
  32.  
  33. short    sidesLeft;
  34.  
  35. /*
  36.  * Square and Side data structure stuff.
  37.  */
  38.  
  39. typedef    struct Square    Square;
  40.  
  41. struct Square
  42. {
  43.     short    owner;        /* index of player owning square.  -1 = not owned yet */
  44.     short    sides;        /* bit map indicating sides that are on. 0 = none, 15 = all */
  45. };
  46.  
  47. static Square    square[maxSquares][maxSquares];
  48.  
  49.  
  50. /*
  51.  * Sides are initialized to -1.  When a player hits them, they are set to
  52.  * the player number (0..maxPlayer-1).  When a side is drawn, it's drawn in
  53.  * the player's color.
  54.  */
  55.  
  56. static short    hSide[maxSquares][maxSquares + 1];
  57. static short    vSide[maxSquares + 1][maxSquares];
  58.  
  59. static RgnHandle    ownerRgn = nil;
  60.  
  61.  
  62. /* Local routine prototypes */
  63.  
  64. static RgnHandle ProtoOwnerRgn (void);
  65. static RgnHandle OwnerRgn (short, short);
  66.  
  67.  
  68. void
  69. GetBoardSize (short *h, short *v)
  70. {
  71.     *h = hSquares;
  72.     *v = vSquares;
  73. }
  74.  
  75.  
  76. void
  77. SetBoardSize (short h, short v)
  78. {
  79.     hSquares = h;
  80.     vSquares = v;
  81. }
  82.  
  83.  
  84. void
  85. InitializeBoard (void)
  86. {
  87. short    h, v;
  88. Square    *s;
  89. Rect    r;
  90. RgnHandle    rgn;
  91.     
  92.     for (h = 0; h < hDots; h++)
  93.     {
  94.         for (v = 0; v < vDots; v++)
  95.         {
  96.             s = &square[h][v];
  97.             s->owner = -1;
  98.             s->sides = 0;
  99.         }
  100.     }
  101.     for (h = 0; h < hSquares; h++)
  102.     {
  103.         for (v = 0; v < vDots; v++)
  104.             hSide[h][v] = -1;
  105.     }
  106.     for (h = 0; h < hDots; h++)
  107.     {
  108.         for (v = 0; v < vSquares; v++)
  109.             vSide[h][v] = -1;
  110.     }
  111.     sidesLeft = (hSquares + 1) * vSquares + hSquares * (vSquares + 1);
  112.  
  113.     /*
  114.      * Calculate prototypical region inside a completed box.  This is the
  115.      * square, minus the overlap of the squares with the links, minus the
  116.      * overlap of the square with the dots at the corners.
  117.      */
  118.  
  119.     if (ownerRgn == nil)
  120.         ownerRgn = ProtoOwnerRgn ();
  121. }
  122.  
  123.  
  124. static RgnHandle
  125. ProtoOwnerRgn (void)
  126. {
  127. Rect    r;
  128. RgnHandle    rgn, rgn2;
  129.  
  130.     SetRect (&r, 1, 1, sideLen, sideLen);
  131.     InsetRect (&r, (linkWidth - 1) / 2, (linkWidth - 1) / 2);
  132.     if ((rgn = NewRgn ()) == nil)
  133.         return (nil);
  134.     if ((rgn2 = NewRgn ()) == nil)
  135.     {
  136.         DisposeRgn (rgn);
  137.         return (nil);
  138.     }
  139.     RectRgn (rgn, &r);
  140.     SetRect (&r, 0, 0, dotSize, dotSize);
  141.     OffsetRect (&r, -(dotSize - 1) / 2, -(dotSize - 1) / 2);
  142.     RectRgn (rgn2, &r);
  143.     DiffRgn (rgn, rgn2, rgn);        /* subtract upper left corner */
  144.     OffsetRgn (rgn2, sideLen, 0);
  145.     DiffRgn (rgn, rgn2, rgn);        /* subtract upper right corner */
  146.     OffsetRgn (rgn2, 0, sideLen);
  147.     DiffRgn (rgn, rgn2, rgn);        /* subtract lower right corner */
  148.     OffsetRgn (rgn2, -sideLen, 0);
  149.     DiffRgn (rgn, rgn2, rgn);        /* subtract lower left corner */
  150.     DisposeRgn (rgn2);
  151.     return (rgn);                /* this must be disposed of by caller */
  152. }
  153.  
  154.  
  155. /*
  156.  * Calculate the region inside a completed box.  This is a copy of the
  157.  * prototypical owner region, offset by the appropriate amount.
  158.  */
  159.  
  160. static RgnHandle
  161. OwnerRgn (short h, short v)
  162. {
  163. RgnHandle    rgn, rgn2;
  164.  
  165.     if ((rgn = NewRgn ()) == nil)
  166.         return (nil);
  167.     CopyRgn (ownerRgn, rgn);
  168.     OffsetRgn (rgn, hPad + h * sideLen, vPad + v * sideLen);
  169.     return (rgn);                /* this must be disposed of by caller */
  170. }
  171.  
  172.  
  173. /*
  174.  * Calculate size of rectangle in which board is drawn.  This is actually
  175.  * inaccurate; it's the recangle that goes from the center of the upper left
  176.  * dot to the center of the lower right dot.
  177.  */
  178.  
  179. void
  180. CalcBoardSize (Rect *r)
  181. {
  182.     SetRect (r, 0, 0, hSquares * sideLen, vSquares * sideLen);
  183. }
  184.  
  185.  
  186. short
  187. SidesLeft (void)
  188. {
  189.     return (sidesLeft);
  190. }
  191.  
  192.  
  193. /*
  194.  * Add a side to a square.
  195.  */
  196.  
  197. static void
  198. AddSide (short h, short v, short mask)
  199. {
  200.     if (h < 0 || h >= hSquares || v < 0 || v >= vSquares)
  201.         return;            /* coordinates out of range */
  202.     square[h][v].sides |= mask;
  203.     if (square[h][v].sides == allMask)
  204.     {
  205.         square[h][v].owner = GetPlayer ();
  206.         DrawOwner (h, v);
  207.     }
  208. }
  209.  
  210.  
  211. static Boolean
  212. WillCompleteBox (short h, short v, short sideMask)
  213. {
  214.     if (h < 0 || h >= hSquares || v < 0 || v >= vSquares)
  215.         return (false);            /* coordinates out of range */
  216.     if (square[h][v].sides == allMask)
  217.         return (false);        /* already complete */
  218.     return ((square[h][v].sides | sideMask) == allMask ? true : false);
  219. }
  220.  
  221.  
  222. short
  223. SetHSide (short h, short v, short player)
  224. {
  225. short    squares;
  226.  
  227.     squares = WillCompleteBox (h, v, topMask);
  228.     squares += WillCompleteBox (h, v - 1, bottomMask);
  229.     hSide[h][v] = player;
  230.     --sidesLeft;
  231.     DrawHSide (h, v);
  232.     AddSide (h, v, topMask);
  233.     AddSide (h, v - 1, bottomMask);
  234.     return (squares);
  235. }
  236.  
  237.  
  238. short
  239. SetVSide (short h, short v, short player)
  240. {
  241. short    squares;
  242.  
  243.     squares = WillCompleteBox (h, v, leftMask);
  244.     squares += WillCompleteBox (h - 1, v, rightMask);
  245.     vSide[h][v] = player;
  246.     --sidesLeft;
  247.     DrawVSide (h, v);
  248.     AddSide (h, v, leftMask);
  249.     AddSide (h - 1, v, rightMask);
  250.     return (squares);
  251. }
  252.  
  253.  
  254. /*
  255.  * Board hit testing routines.  These are not the most efficient, algorithmically,
  256.  * but they make fairly clear what is being tested through the use of a rect that
  257.  * is moved around to the positions between pairs of adjacent dots.  The hittable
  258.  * areas between dots are considered to be as wide as the dot although the links
  259.  * themselves are only drawn 3 pixels wide.  This is to make them easier to hit.
  260.  */
  261.  
  262.  
  263. Boolean
  264. HLinkHitTest (Point pt, short    *hval, short    *vval)
  265. {
  266. short    h, v;
  267. Rect    r, r2;
  268.  
  269.     /* calculate rect between two leftmost dots in upper row */
  270.     SetRect (&r, 0, 0, sideLen - dotSize, dotSize);
  271.     OffsetRect (&r, hPad + (dotSize + 1) / 2, vPad - (dotSize - 1) / 2);
  272.     
  273.     for (v = 0; v < vDots; v++)                /* for each row */
  274.     {
  275.         r2 = r;
  276.         for (h = 0; h < hSquares; h++)        /* for each column in row */
  277.         {
  278.             if (PtInRect (pt, &r2))
  279.             {
  280.                 if (hSide[h][v] != noPlayer)    /* it's already been clicked */
  281.                     return (false);
  282.                 *hval = h;
  283.                 *vval = v;
  284.                 return (true);
  285.             }
  286.             OffsetRect (&r2, sideLen, 0);    /* move to next column */
  287.         }
  288.         OffsetRect (&r, 0, sideLen);        /* move to next row */
  289.     }
  290.     return (false);
  291. }
  292.  
  293.  
  294. Boolean
  295. VLinkHitTest (Point pt, short    *hval, short    *vval)
  296. {
  297. short    h, v;
  298. Rect    r, r2;
  299.  
  300.     /* calculate rect between two uppermost dots in left row */
  301.     SetRect (&r, 0, 0, dotSize, sideLen - dotSize);
  302.     OffsetRect (&r, hPad - (dotSize - 1) / 2, vPad + (dotSize + 1) / 2);
  303.     
  304.     for (h = 0; h < hDots; h++)                /* for each row */
  305.     {
  306.         r2 = r;
  307.         for (v = 0; v < vSquares; v++)        /* for each column */
  308.         {
  309.             if (PtInRect (pt, &r2))
  310.             {
  311.                 if (vSide[h][v] != noPlayer)    /* it's already been clicked */
  312.                     return (false);
  313.                 *hval = h;
  314.                 *vval = v;
  315.                 return (true);
  316.             }
  317.             OffsetRect (&r2, 0, sideLen);    /* move to next row */
  318.         }
  319.         OffsetRect (&r, sideLen, 0);        /* move to next column */
  320.     }
  321.     return (false);
  322. }
  323.  
  324.  
  325. /*
  326.  * Board drawing routines.  DrawHSide() and DrawVSide() are called only for
  327.  * sides that have been clicked.
  328.  */
  329.  
  330. void
  331. DrawHSide (short h, short v)
  332. {
  333. short    len;
  334.  
  335.     SetColor (GetPlayerColor (hSide[h][v]));
  336.     PenSize (1, linkWidth);
  337.     h = hPad + h * sideLen + (dotSize + 1) / 2;
  338.     v = vPad + v * sideLen - (linkWidth - 1) / 2;
  339.     MoveTo (h, v);
  340.     LineTo (h + sideLen - dotSize - 1, v);
  341.     PenNormal ();
  342.     RestoreColor ();
  343. }
  344.  
  345.  
  346. void
  347. DrawVSide (short h, short v)
  348. {
  349. short    len;
  350.  
  351.     SetColor (GetPlayerColor (vSide[h][v]));
  352.     PenSize (linkWidth, 1);
  353.     h = hPad + h * sideLen - (linkWidth - 1) / 2;
  354.     v = vPad + v * sideLen + (dotSize + 1) / 2;
  355.     MoveTo (h, v);
  356.     LineTo (h, v + sideLen - dotSize - 1);
  357.     PenNormal ();
  358.     RestoreColor ();
  359. }
  360.  
  361.  
  362. void
  363. DrawGrid (short cols, short rows,
  364.                 short hOffset, short vOffset,
  365.                 short hSize, short vSize)
  366. {
  367. short    i, h, v, len;
  368.  
  369.     /*  draw vertical lines  */
  370.     len = (rows - 1) * vSize;
  371.     for (i = 0; i < cols; i++)
  372.     {
  373.         h = hOffset + i * hSize;
  374.         MoveTo (h, vOffset);
  375.         LineTo (h, vOffset + len);
  376.     }
  377.     /*  draw horizontal lines  */
  378.     len = (cols - 1) * hSize;
  379.     for (i = 0; i < rows; i++)
  380.     {
  381.         v = vOffset + i * vSize;
  382.         MoveTo (hOffset, v);
  383.         LineTo (hOffset + len, v);
  384.     }
  385. }
  386.  
  387.  
  388. void
  389. DrawOwner (short h, short v)
  390. {
  391. Str255    s;
  392. short    width;
  393. RgnHandle    rgn;
  394. short        owner = square[h][v].owner;
  395.  
  396.     if (owner != -1)
  397.     {
  398.         SetColor (GetPlayerColor (owner));
  399.         if ((rgn = OwnerRgn (h, v)) != nil)
  400.         {
  401.             InsetRgn (rgn, 1, 1);
  402.             FrameRgn (rgn);
  403.             DisposeRgn (rgn);
  404.         }
  405.         NumToString ((long) owner + 1, s);
  406.         width = StringWidth (s);
  407.         MoveTo (hPad + h * sideLen + (sideLen- width) / 2,
  408.                 vPad + (v+1) * sideLen - (sideLen - fontHeight) / 2 - fontInfo.descent);
  409.         DrawString (s);
  410.         RestoreColor ();
  411.     }
  412. }
  413.  
  414.  
  415. void
  416. DrawBoard (void)
  417. {
  418. short    h, v;
  419. Rect    r;
  420.  
  421.     SetColor (GetSideColor ());
  422.     PenPat (gray);
  423.     DrawGrid (hDots, vDots, hPad, vPad, sideLen, sideLen);
  424.     PenNormal ();
  425.     SetColor (GetDotColor ());
  426.     for (h = 0; h < hDots; h++)
  427.     {
  428.         for (v = 0; v < vDots; v++)
  429.         {
  430.             SetRect (&r, 0, 0, dotSize, dotSize);
  431.             OffsetRect (&r, hPad - (dotSize - 1) / 2 + h * sideLen,
  432.                             vPad - (dotSize - 1) / 2 + v * sideLen);
  433.             PaintRect (&r);
  434.         }
  435.     }
  436.     RestoreColor ();
  437.     for (h = 0; h < hSquares; h++)
  438.     {
  439.         for (v = 0; v < vDots; v++)
  440.         {
  441.             if (hSide[h][v] != -1)
  442.                 DrawHSide (h, v);
  443.         }
  444.     }
  445.     for (h = 0; h < hDots; h++)
  446.     {
  447.         for (v = 0; v < vSquares; v++)
  448.         {
  449.             if (vSide[h][v] != -1)
  450.                 DrawVSide (h, v);
  451.         }
  452.     }
  453.     for (h = 0; h < hSquares; h++)
  454.     {
  455.         for (v = 0; v < vSquares; v++)
  456.             DrawOwner (h, v);
  457.     }
  458. }
  459.